package first_assignment;

import java.util.Random;
import java.util.Vector;

import first_assignment.Consts_1st.*;

/**
 * a class containing the movie collection, and the NewDay() function.
 * 
 * @author Peretz Oded, Castel Maayan, Farchy Ofir. 
 */
public class MovieCollection {
	// a random number creator (probability dependent).
	private DistributionCreator mc;
	
	// the movie list.
	private Vector<Movie> movieNameList;
	
	// an array for distribution tests.
	private Vector<Integer> testDistArray;
	
	// number of new movies each day.
	public int numOfNewMovies;

	/**
	 * c'tor - initialize movie list according to requirements.
	 * initialize the number of new movies to be added each day.
	 * @param S - a movie collection (string vector) to store.
	 */
	public MovieCollection(Vector<Movie> S) {
		numOfNewMovies = (int) java.lang.Math.ceil(Consts_1st.NewMoviesFactor * S.size());
		mc = new DistributionCreator();
		mc.InitMovieSet(Consts_1st.a, Consts_1st.M);
		testDistArray = new Vector<Integer>(mc.getPopularityClassNumber());
		
		for (int i=0; i<mc.getPopularityClassNumber(); ++i) {
			testDistArray.add(0);
		}
		
		movieNameList = S;
	}
	
	public MovieCollection(Vector<Movie> S, DistributionCreator dist, double factor) {
		numOfNewMovies = (int) java.lang.Math.ceil(factor * S.size());
		mc = dist;
		testDistArray = new Vector<Integer>(mc.getPopularityClassNumber());
		
		for (int i=0; i<mc.getPopularityClassNumber(); ++i) {
			testDistArray.add(0);
		}

		movieNameList = S;
	}
	
	/**
	 * printout function (mostly) for testing.
	 */
	public void PrintRandomMovie() {
		int index = mc.getRandomNumber(Consts_1st.M)-1,
			totalCount = 0;
		System.out.println("movie name: " + movieNameList.elementAt(movieNameList.size()-index-1).movieName);
		for (int i=0; i<mc.getPopularityClassNumber(); ++i) {
			totalCount += mc.getPopularityClassSize(i);
			if (index<=totalCount) {
				testDistArray.set(i, testDistArray.get(i) + 1);
				return;
			}
		}
	}

	/**
	 * printout function (mostly) for testing.
	 */
	public void PrintAllMovies() {
		for (int i=0; i<movieNameList.size(); ++i) {
			System.out.print(movieNameList.elementAt(i).movieName + ", ");
			if (0 == (i+1)%20) { System.out.println(" -- line #" + (i+1)/20); }
		}
		System.out.println("list size: " + movieNameList.size());
	}

	/**
	 * movie list setter.
	 * @param movieNameList - a movie list to store.
	 */
	public void setMovieNameList(Vector<Movie> movieNameList) {
		this.movieNameList = movieNameList;
	}

	/**
	 * movie list getter.
	 * @return the stored movie list.
	 */
	public Vector<Movie> getMovieNameList() {
		return movieNameList;
	}

	/**
	 * calculates a uniformly distributed random index within the given boundaries.
	 * @param lowBound - the lower boundary of the desired interval (inclusive).
	 * @param highBound - the upper boundary of the desired interval (exclusive).
	 * @return a uniformly distributed random index.
	 */
	private int RandomBoundedIndex(int lowBound, int upperBound) {
		final Random generator = new Random();
		return generator.nextInt(upperBound - lowBound) + lowBound;
	}
	
	/**
	 * updates the movie list with new movies with top priority,
	 * while removing low priority old movies.
	 * @param newMovies - a vector of movies from which we choose new
	 * movies to add each day, changes are made to this input parameter!
	 * @return true for success false otherwise.
	 */
	public Vector<Movie> NewDay(Vector<Movie> newMovies) {
		// temporary movie variable for swapping movie places across the set.
		Movie tmpMovie;
		// a random index variable
		int randIndex,
		// the number of different popularity classes.
		popClassNum = mc.getPopularityClassNumber();
		// index boundaries of each popularity class.
		int upperBound, lowerBound = 0;
		// the list of movies removed from the database (lowest priority)
		Vector<Movie> removedMovies = new Vector<Movie>(numOfNewMovies);
		
		// validations
		if (null == newMovies || numOfNewMovies > newMovies.size() || 0 >= popClassNum) { return null; }
		
		// probability changes of movies in the original movie list
		for (int i=popClassNum-1; (0 <= i) && (numOfNewMovies < mc.getPopularityClassSize(i)); --i) {
			upperBound= lowerBound + mc.getPopularityClassSize(i) - 1;
			for (int j=0; j < numOfNewMovies; ++j) {
				randIndex = RandomBoundedIndex(lowerBound, upperBound);
				tmpMovie = movieNameList.remove(randIndex);
				movieNameList.add(lowerBound, tmpMovie);
			}
			lowerBound = upperBound + 1;
		}
		// the insertion of new movies (randomly) and extraction of low probability ones.
		for (int i=0; i<numOfNewMovies; ++i) {
			// insert a new movie (randomly choose one)
			randIndex = RandomBoundedIndex(0, newMovies.size()); 
			movieNameList.add(movieNameList.size(), newMovies.elementAt(randIndex));
			removedMovies.add(0, movieNameList.remove(0));
			newMovies.remove(randIndex);
		}
		testDistArray = new Vector<Integer>(mc.getPopularityClassNumber());
		for (int i=0; i<mc.getPopularityClassNumber(); ++i) {
			testDistArray.add(0);
		}
		return removedMovies;
	}
	
	// distribution array examination. for tests.
	public void PrintDistArray() {
		System.out.println("The probability array:");
		for (int i=0; i<testDistArray.size(); ++i) {
			System.out.print(mc.getPopularityClassProbabilty(i) + ", ");
		}
		System.out.println();
		System.out.println("The size array:");
		for (int i=0; i<testDistArray.size(); ++i) {
			System.out.print(mc.getPopularityClassSize(i) + ", ");
		}
		System.out.println();
		System.out.println("The actual distribution:");
		for (int i=0; i<testDistArray.size(); ++i) {
			System.out.print(testDistArray.get(i) + ", ");
		}
		System.out.println();
	}
	
}
